﻿using System;
using UtilZ.Dotnet.Ex.DataStruct;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.DataTransfer.Base
{
    /// <summary>
    /// 数据传输接口
    /// </summary>
    public abstract class DataTransferAbs : IDataTransfer
    {
        private readonly AsynQueue<TransferDataItem> _asyncSendQueue;


        /// <summary>
        /// 构造函数
        /// </summary>
        public DataTransferAbs()
        {
            var option = new AsynQueueOptions()
            {
                FullMode = ContainerFullMode.DropOld,
                AutoStart = false,
                ThreadName = $"{this.GetType().FullName}_异步发送数据队列线程"
            };
            this._asyncSendQueue = new AsynQueue<TransferDataItem>(this.AsyncSendQueueCallback, option);
        }




        /// <summary>
        /// 接收到数据输出事件
        /// </summary>
        public event EventHandler<DataTransferDataOutputArgs> ReceiveData;

        /// <summary>
        /// 调用接收到数据输出事件
        /// </summary>
        /// <param name="args">事件参数</param>
        protected void OnRaiseReceiveData(DataTransferDataOutputArgs args)
        {
            this.ReceiveData?.Invoke(this, args);
        }



        private event EventHandler<AsynSendCompletedArgs> _asyncSendCompleted = null;
        /// <summary>
        /// 异步发送完成事件
        /// </summary>
        public event EventHandler<AsynSendCompletedArgs> AsyncSendCompleted
        {
            add { this._asyncSendCompleted += value; }
            remove { this._asyncSendCompleted -= value; }
        }


        private readonly AsynSendCompletedArgs _asynSendCompletedArgs = new AsynSendCompletedArgs();
        /// <summary>
        /// 更新结果
        /// </summary>
        /// <param name="rid">发送资源标识</param>
        /// <param name="ssynSendResult">指示异步发送结果成功或失败[true:成功;false:失败]</param>
        /// <param name="exception">异常</param>
        protected void OnRaiseAsyncSendCompleted(int rid, DataTransferResult ssynSendResult, Exception exception)
        {
            if (this._asyncSendCompleted != null)
            {
                this._asynSendCompletedArgs.Update(rid, ssynSendResult, exception);
                try
                {
                    this._asyncSendCompleted.Invoke(this, this._asynSendCompletedArgs);
                }
                catch (ArgumentNullException auex)
                {
                    ZLoger.Warn(auex, "外部取消了已注册的事件.");
                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex);
                }
            }
        }





        /// <summary>
        /// Tag
        /// </summary>
        public object Tag { get; set; }




        /// <summary>
        /// 开始收发数据
        /// </summary>
        public void Start()
        {
            this.PrimitiveStart();
            this._asyncSendQueue.Start();
        }

        /// <summary>
        /// 开始收发数据
        /// </summary>
        protected abstract void PrimitiveStart();




        /// <summary>
        /// 让步收发数据
        /// </summary>
        public void Stop()
        {
            this.PrimitiveStop();
            this._asyncSendQueue.Stop();
        }

        /// <summary>
        /// 让步收发数据
        /// </summary>
        protected abstract void PrimitiveStop();



        private readonly SingleTypeObjectPool<TransferDataItem> _transferDataItemPool = new SingleTypeObjectPool<TransferDataItem>();

        /// <summary>
        /// 获取传输对象
        /// </summary>
        /// <returns>传输对象</returns>
        private TransferDataItem GetTransferDataItem()
        {
            TransferDataItem transferDataItem;
            if (!this._transferDataItemPool.TryGet(out transferDataItem))
            {
                transferDataItem = new TransferDataItem();
            }

            return transferDataItem;
        }

        /// <summary>
        /// 添加传输项
        /// </summary>
        /// <param name="transferDataItem">传输对象</param>
        private void AddTransferDataItem(TransferDataItem transferDataItem)
        {
            this._transferDataItemPool.Add(transferDataItem);
        }






        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        /// <param name="options">发送选项</param>
        public void Send(byte[] buffer, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(buffer, 0, buffer.Length, options);
            this.Send(transferDataItem);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        /// <param name="startIndex">发送数据起始位置</param>
        /// <param name="count">发送数据长度</param>
        /// <param name="options">发送选项</param>
        public void Send(byte[] buffer, int startIndex, int count, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(buffer, startIndex, count, options);
            this.Send(transferDataItem);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bufferData">发送的数据</param>
        /// <param name="options">发送选项</param>
        public void Send(BufferData bufferData, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(bufferData, options);
            this.Send(transferDataItem);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">要发送的数据对象</param>
        /// <param name="options">发送选项</param>
        public void Send(TransferDataAbs data, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(data, options);
            this.Send(transferDataItem);
        }


        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="transferDataItem">发送的选项</param>
        private void Send(TransferDataItem transferDataItem)
        {
            try
            {
                this.PrimitiveSend(transferDataItem);
            }
            finally
            {
                this.AddTransferDataItem(transferDataItem);
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="transferDataItem">发送的选项</param>
        protected abstract void PrimitiveSend(TransferDataItem transferDataItem);









        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        /// <param name="options">发送选项</param>
        /// <returns>返回送资源标识</returns>
        public int SendAsync(byte[] buffer, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(buffer, 0, buffer.Length, options);
            this.SendAsync(transferDataItem);
            return transferDataItem.SID;
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        /// <param name="startIndex">发送数据起始位置</param>
        /// <param name="count">发送数据长度</param>
        /// <param name="options">发送选项</param>
        /// <returns>返回送资源标识</returns>
        public int SendAsync(byte[] buffer, int startIndex, int count, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(buffer, startIndex, count, options);
            this.SendAsync(transferDataItem);
            return transferDataItem.SID;
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="bufferData">发送的数据</param>
        /// <param name="options">发送选项</param>
        /// <returns>返回送资源标识</returns>
        public int SendAsync(BufferData bufferData, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(bufferData, options);
            this.SendAsync(transferDataItem);
            return transferDataItem.SID;
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="data">要发送的数据对象</param>
        /// <param name="options">发送选项</param>
        /// <returns>返回送资源标识</returns>
        public int SendAsync(TransferDataAbs data, ITransferDataOptions options)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            TransferDataItem transferDataItem = this.GetTransferDataItem();
            transferDataItem.Write(data, options);
            this.SendAsync(transferDataItem);
            return transferDataItem.SID;
        }




        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="transferDataItem">发送的选项</param>
        private void SendAsync(TransferDataItem transferDataItem)
        {
            var list = this._asyncSendQueue.Enqueue(transferDataItem);
            if (list != null && list.Count > 0)
            {
                foreach (var item in list)
                {
                    this.OnRaiseAsyncSendCompleted(item.SID, DataTransferResult.Fail, null);
                    this.AddTransferDataItem(item);
                }
            }

        }

        private void AsyncSendQueueCallback(TransferDataItem transferDataItem, object obj)
        {
            try
            {
                this.PrimitiveSend(transferDataItem);
                this.OnRaiseAsyncSendCompleted(transferDataItem.SID, DataTransferResult.Succes, null);
            }
            catch (TimeoutException tex)
            {
                ZLoger.Warn(tex);
                this.OnRaiseAsyncSendCompleted(transferDataItem.SID, DataTransferResult.Fail, tex);
            }
            catch (OperationCanceledException ocex)
            {
                ZLoger.Warn(ocex);
                this.OnRaiseAsyncSendCompleted(transferDataItem.SID, DataTransferResult.Fail, ocex);
            }
            catch (Exception ex)
            {
                ZLoger.Warn(ex);
                this.OnRaiseAsyncSendCompleted(transferDataItem.SID, DataTransferResult.Fail, ex);
            }
            finally
            {
                this.AddTransferDataItem(transferDataItem);
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="transferDataItem">发送的选项</param>
        protected abstract void PrimitiveSendAsync(TransferDataItem transferDataItem);



        /// <summary>
        /// 取消发送
        /// </summary>
        /// <param name="sid">资源标识</param>
        public void CancellSend(int sid)
        {
            this.PrimitiveCancellSend(sid);
        }

        /// <summary>
        /// 取消发送
        /// </summary>
        /// <param name="sid">资源标识</param>
        protected abstract void PrimitiveCancellSend(int sid);











        /// <summary>
        /// 
        /// </summary>
        protected readonly object _disposeLock = new object();

        /// <summary>
        /// 对象释放标识[true:已释放;false:未释放]
        /// </summary>
        protected bool _disposed = false;
        /// <summary>
        /// 获取当前对象是否已释放[true:已释放;false:未释放]
        /// </summary>
        public bool Disposabled
        {
            get
            {
                return _disposed;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            lock (this._disposeLock)
            {
                if (this._disposed)
                {
                    return;
                }
                this._disposed = true;

                this._asyncSendQueue.Dispose();

                this.Dispose(true);

                var list = this._transferDataItemPool.Clear();
                if (list != null)
                {
                    foreach (var item in list)
                    {
                        item.Dispose();
                    }
                }

                GC.SuppressFinalize(true);
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {

        }

    }



}
